package com.jxpanda.commons.toolkit.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.jxpanda.commons.constant.DateTimeConstant;
import com.jxpanda.commons.constant.StringConstant;
import com.jxpanda.commons.formatter.DateTimeFormatter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Map;

/**
 * Json序列化/反序列化工具
 * <p>
 * 【可选功能】调用 registerImageModule(String domain) 函数，增加处理图片相对路径 --> 绝对路径的功能
 * -- 此功能只做序列化的转换，反序列化不处理，功能一旦启用就无法关闭（暂时没有提供关闭功能）
 * -- 重新调用 registerImageModule(String domain) 函数 传入新的域名可以替换域名
 * -- 调用 addImageField(String field) 增加被代理的图片字段，remove同理
 * -- 默认代理的字段有
 * "image", "images", "imageList",
 * "img", "imgList",
 * "picture", "pictures", "pictureList",
 * "pic", "pics", "picList"
 *
 * @author Panda
 */
@SuppressWarnings("unchecked")
public class JsonKit {

    /**
     * 把对象转为json字符串
     */
    @SneakyThrows
    public static String toJson(Object object) {
        return jackson().writeValueAsString(object);
    }

    /**
     * 把对象转为json字符串
     */
    @SneakyThrows
    public static Map<String, Object> toMap(Object object) {
        if (object instanceof Map) {
            return (Map<String, Object>) object;
        }
        // 做两次转换，转为map
        return fromJson(toJson(object));
    }


    /**
     * 把json字符串转为Map
     */
    @SneakyThrows
    public static Map<String, Object> fromJson(String json) {
        return fromJson(json, new TypeReference<>() {
        });
    }

    /**
     * 把json字符串转为对象
     */
    @SneakyThrows
    public static <T> T fromJson(String json, Class<T> clazz) {
        return jackson().readValue(json, clazz);
    }

    /**
     * 把json字符串转为对象
     * 取值跟着泛型走，这个可以传递List进来
     */
    @SneakyThrows
    public static <T> T fromJson(String json, TypeReference<T> typeReference) {
        return jackson().readValue(json, typeReference);
    }

    /**
     * 懒加载单例模式
     */
    public static ObjectMapper jackson() {
        return JacksonSingleton.INSTANCE;
    }

    public static JavaTimeModule javaTimeModule() {
        return JavaTimeModuleSingleton.INSTANCE;
    }

    private static ImageModule imageModule(String domain) {
        return ImageModuleSingleton.INSTANCE.withDomain(domain);
    }

    /**
     * 注册图片域名模块
     * 如果已经注册了，会替换域名
     * 如果没有注册过，就注册到Jackson上
     */
    public static void registerImageModule(String domain) {
        if (jackson().getRegisteredModuleIds().contains(ImageModule.class.getName())) {
            ImageModuleSingleton.INSTANCE.setDomain(domain);
        } else {
            jackson().registerModule(imageModule(domain));
            // 这里是Jackson的一个坑， 如果在注册序列化Module之前使用过jackson来序列化对象
            // jackson内部会有缓存机制，把各个类型的SerializerProvider都缓存起来
            // 导致后添加的Module不生效，所以注册新的Module之后，要重新new一个Provider出来，手动让内部缓存全部失效
            // FIXME: 此问题在升级jackson版本到2.12.4之后得到解决，因此注释掉了，代码暂时不删除，为以后保留一个记录
            // jackson().setSerializerProvider(new DefaultSerializerProvider.Impl());
        }
    }

    public static void addImageField(String field) {
        ImageModuleSingleton.INSTANCE.addField(field);
    }

    public static void removeImageField(String field) {
        ImageModuleSingleton.INSTANCE.removeField(field);
    }

    /**
     * 创建ObjectMapper对象的单例模式
     */
    @Slf4j
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class JacksonSingleton {
        private static final ObjectMapper INSTANCE;

        static {
            INSTANCE = new ObjectMapper();
            INSTANCE.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
            INSTANCE.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
            INSTANCE.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
            INSTANCE.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

            INSTANCE.registerModule(javaTimeModule());
            INSTANCE.getSerializerProvider().setNullValueSerializer(new NullSerializer());
        }

    }

    /**
     * 创建ObjectMapper对象的单例模式
     */
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    private static final class JavaTimeModuleSingleton {
        private static final JavaTimeModule INSTANCE;

        static {
            INSTANCE = new JavaTimeModule();
            // LocalDateTime序列化，约定，如果值是1970-01-01 00:00:00则序列化为空字符串
            INSTANCE.addSerializer(LocalDateTime.class, new JsonSerializer<>() {
                @Override
                @SneakyThrows
                public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) {
                    String serializedValue = StringConstant.BLANK;
                    if (value.isAfter(DateTimeConstant.DATE_1970_0_0)) {
                        serializedValue = DateTimeFormatter.NORMAL_DATE_TIME.getFormatter().format(value);
                    }
                    gen.writeString(serializedValue);
                }
            });
            // LocalDate序列化，约定，如果值是1970-01-01则序列化为空字符串
            INSTANCE.addSerializer(LocalDate.class, new JsonSerializer<>() {
                @Override
                @SneakyThrows
                public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) {
                    String serializedValue = StringConstant.BLANK;
                    if (value.isAfter(DateTimeConstant.DATE_1970_0_0.toLocalDate())) {
                        serializedValue = DateTimeFormatter.NORMAL_DATE.getFormatter().format(value);
                    }
                    gen.writeString(serializedValue);
                }
            });
            INSTANCE.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.NORMAL_TIME.getFormatter()));

            INSTANCE.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.NORMAL_DATE_TIME.getFormatter()));
            INSTANCE.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.NORMAL_DATE.getFormatter()));
            INSTANCE.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.NORMAL_TIME.getFormatter()));
        }
    }

    private static final class ImageModuleSingleton {
        private static final ImageModule INSTANCE;

        static {
            INSTANCE = new ImageModule();
        }
    }

}
